package com.topnav.mc.exception;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.topnav.mc.admin.entity.SystemLog;
import com.topnav.mc.admin.service.ISystemLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ResponseBody;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.js.lib.annotation.aop.ApiLoggerAop.LoggerVO;
import com.js.lib.exception.CustomException;
import com.js.lib.log.Constant;
import com.js.lib.log.LogService;
import com.js.lib.log.LogTypeEnum;
import com.js.lib.result.Result;
import com.js.lib.utils.LoggerUtils;
import com.topnav.mc.uitls.ErrorResultUtil;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.UnexpectedTypeException;
import org.slf4j.MDC;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import static com.topnav.mc.exception.ExceptionMsg.*;

/**
 * 说明：全局异常Handler
 * @类名: ExceptionHandler
 *
 * @author   kenny
 * @Date	 2019年12月27日上午9:49:47
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
	private String commLogMsg = "日志信息: {}" ;
	private String commErrorMsg = "日志信息: {}，异常信息: {}" ;


	/** 这个日志实现都一样，主要是为了区分写到不同的日志文件里*/
	@Resource(name="globalExceptionLogServiceImpl")
	private LogService logService;
	@Autowired
	private ISystemLogService systemLogService;

   @ExceptionHandler(value = Exception.class)
    public Result runtimeException(Exception e,HttpServletRequest request) {
	   	Map<String,Object>logParam = null;
	   	Result result = null;
		try{
	    	//日志的通用参数
			logParam = LoggerUtils.generalParam();
	    	logParam.put("info", getCommMsg(e.toString()));
	    	logService.log(logParam,null);
	    	/** 未定义异常：%s */
	    	log.error(">>>>>> {}",e.toString());
	    	result = ErrorResultUtil.result(EXCEPTION,e.toString());
	    }catch (Exception e1){
	    	log.debug(">>>>>异常 {}",e1);
	    }
		log(result,e.toString());
		return result;

    }

	/** 数据上报错误 */
	@ExceptionHandler(value = DocWriteFromSiteRuntimeException.class)
	public Result iocalSaveRuntimeException(DocWriteFromSiteRuntimeException e,HttpServletRequest request) {
		Result result = null;
		try {
			SystemLog systemLog = new SystemLog();
			/** 采集站上报 */
			systemLog.setTopic(1);
			systemLog.setKeyWord(e.getKeyWord());
			systemLog.setLogLevel(e.getLevel());
			if (e.getMessage() != null){
				if (e.getMessage().length() > 254)
					systemLog.setMessage(e.getMessage().substring(0, 254));
				else
					systemLog.setMessage(e.getMessage());
			}
			systemLog.setCreateTime(new Date());
			systemLog.setUpdateTime(new Date());
			systemLogService.save(systemLog);
			return Result.error(e.getMessage());
			/** 未定义异常：%s */
		} catch (Exception e1) {
			log.debug(">>>>>异常 {}", e1);
			return Result.error(e1.getMessage());
		}
	}
    /*=============请求错误 start==============================================*/

    /**
     * HTTP 请求方式不支持异常
     * HttpRequestMethodNotSupportedException
     * @return {@link Result}
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public Result httpRequestMethodNotSupportException(HttpRequestMethodNotSupportedException e,
    		HttpServletRequest request) {

    	Map<String,Object>logParam = null;
    	Result result = null;
    	String msg = String.format("不支持%s请求方法", request.getMethod());
		try{
	    	//日志的通用参数
			logParam = LoggerUtils.generalParam();
			log.error(">>>>>> {}",String.format(ERROR_COMMON_REQUEST_EXCEPTION.getDesc(),msg));
	    	result = ErrorResultUtil.result(ERROR_COMMON_REQUEST_EXCEPTION);
	    	logParam.put(commLogMsg, result);
	    	logService.log(logParam,null);
	    }catch (Exception e1){
	    	logParam.put("info",this.getErrorMsg(msg,e1.toString()));
	    	log.debug(">>>>>异常 {}",e1);
	    }
		log(result,e.getMessage());
		return result;

    }

    /**
     * 媒体类型不支持
     * @return {@link Result}
     */
    @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
    public Result httpMediaTypeNotSupportException(HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
    	Map<String,Object>logParam = null;
    	Result result = null;
    	String msg = String.format("不支持%s媒体类型", request.getContentType());
		try{
			logParam = LoggerUtils.generalParam();
			msg = String.format("不支持%s媒体类型", request.getContentType());
			result = ErrorResultUtil.result(ERROR_COMMON_REQUEST_EXCEPTION,msg);
	    	logParam.put("result", result);
		}catch (Exception e1){
	    	logParam.put("result",this.getErrorMsg(msg,e1.toString()));
	    	log.debug(">>>>>异常 {}",e1);
	    }
		logService.log(logParam,null);
        return result;
    }


    /**
     * JSON 格式化异常
     * @return {@link Result}
     * @throws IOException
     */
    @ExceptionHandler(value = JsonProcessingException.class)
    public Result jsonMappingException(JsonProcessingException e, HttpServletRequest request) {
    	Result result=null;
    	try{
        	BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
        	StringBuilder responseStrBuilder = new StringBuilder();
        	String inputStr;
        	while ((inputStr = streamReader.readLine()) != null)
        		responseStrBuilder.append(inputStr);
        	responseStrBuilder.toString();
        	String msg = responseStrBuilder.toString();//String.format("不支持%s媒体类型", request.getContentType());
			log.error(">>>>>> {}",String.format(ERROR_PARAM_JSON_INCORRECT.getDesc(),msg));
            result = ErrorResultUtil.result(ERROR_PARAM_JSON_INCORRECT);
            logService.log(result,null);
    	}catch (IOException e1){
			log.error(">>>>>> {}",String.format(ERROR_PARAM_JSON_INCORRECT.getDesc(),e1.toString()));
    		result = ErrorResultUtil.result(ERROR_PARAM_JSON_INCORRECT);
    		logService.log(result,e.toString());
    		log.debug(">>>>>异常 {}",e1);
    	}
    	return result;
     }

    /**
     * Http 数据不可读
     * @param e
     * @return
     */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public Result httpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
    	log.error(e.toString());
    	Result result=null;
    	Map<String,Object>logParam = null;
    	String msg = "可能是非json数据错误，也可能是某参数的值超出范围";
    	log.error(String.format(ERROR_DATA_NOT_READABLE_EXCEPTION.getDesc(),msg));
    	result = ErrorResultUtil.result(ERROR_DATA_NOT_READABLE_EXCEPTION);
    	try{
    		logParam = LoggerUtils.generalParam();
            logParam.put("result", result);
            logService.log(logParam,null);
    	}catch (Exception e1){
    		log.debug(">>>>>异常 {}",e1);
     	}
         return result;
    }

    /**
     * 非法参数异常
     * @return {@link Result}
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result illegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
    	Result result=null;
    	try{
    		log.error(">>>>>> {}",EXCEPTION,e.toString());
            result = ErrorResultUtil.result(EXCEPTION,e.toString());
    	}catch(Exception e1){
			log.error(">>>>>> {}",EXCEPTION,e1.toString());
    		result = ErrorResultUtil.result(EXCEPTION,e1.toString());
    		logService.log(result,e.toString());
    	}
        return result;
    }

    /**
     * 方法参数校验异常
     * @return {@link Result}
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result methodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String traceCode = MDC.get(Constant.LOG_TRACE);
        BindingResult bindingResult = e.getBindingResult();
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        StringBuilder stringBuilder = new StringBuilder();
        for (ObjectError objectError : allErrors) {
        	//ObjectError是个基类，但时用到子类的field，对象转换为map
        	//JSONObject jsonObject = JSONObject.parseObject(objectError.toString());
			String jStr = JSON.toJSONString(objectError);

        	JSONObject jsonObject = JSON.parseObject(jStr);//(JSONObject)objectError;
            //Map<Object, Object> map = (Map<Object, Object>)jsonObject;
            //Object field = map.get("field");
            Object field = jsonObject.get("field");
            if (field != null){
            	stringBuilder.append(String.format("(%s)", field));
            }
            stringBuilder.append(jsonObject.get("defaultMessage"));
            stringBuilder.append(";");
        }
        //log.error(stringBuilder.toString());
		log.error(String.format(ERROR_COMMON_VALIDATION_EXCEPTION.getDesc(),stringBuilder.toString()));
        return ErrorResultUtil.result(ERROR_COMMON_VALIDATION_EXCEPTION);
    }

    /**
     * 参数类型异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = UnexpectedTypeException.class)
    public Result unexpectedTypeException(UnexpectedTypeException e, HttpServletRequest request) {
    	log.error(">>>>>> {}",String.format(ERROR_PARAM_UNEXPECTED_TYPE.getDesc(),e.getMessage()));
    	return ErrorResultUtil.result(ERROR_PARAM_UNEXPECTED_TYPE);
    }

    /**
     * 自定义异常类
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(value = CustomException.class)
    public Result customException(CustomException e, HttpServletRequest request) {
    	//Result result;
    	if (e.getExtendMsg() != null && e.getExtendMsg().length > 0)
    		return Result.error(e.getCode(),String.format(e.getMsg(),e.getExtendMsg()));
    		//result= ResultUtil.result(CustomException,e.getExtendMsg());
    	else
    		return Result.error(e.getCode(),String.format(e.getMsg()));
    		//result= ResultUtil.result(CustomException);
    	//return result;
    }

    private String getCommMsg(String ... args){
    	return String.format(commLogMsg, args);
    }
    private String getErrorMsg(String ... args){
    	return String.format(commErrorMsg, args);
    }


    /**
     * 取得body的参数
     * @param request
     * @return
     * @throws UnsupportedEncodingException
     * @throws IOException
     */
    private String getParamData(HttpServletRequest request) throws UnsupportedEncodingException, IOException{
       	BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
    	StringBuilder responseStrBuilder = new StringBuilder();
    	String inputStr;
    	while ((inputStr = streamReader.readLine()) != null)
    		responseStrBuilder.append(inputStr);
    	responseStrBuilder.toString();
    	return responseStrBuilder.toString();
    }


    /**
     * 输出到日志文件
     * @param result
     * @param exceptionMsg
     */
    private void log(Result result,String exceptionMsg){
    	Map<String,Object> map = LoggerUtils.generalParam();
    	LoggerVO lv = new LoggerVO().setTime(new Date())
    			.setTradeCode(map.get("traceCode")==null?null:map.get("traceCode").toString())
    			.setIp(map.get("clientIp")==null?null:map.get("clientIp").toString())
    			.setUrl(map.get("url")==null?null:map.get("url").toString())
    			.setData(result)
    			.setLogType(LogTypeEnum.after);
    	this.log(lv,exceptionMsg);
    }

    /**
     * 输出到日志文件
     * @param loggerVO
     * @param exceptionMsg
     */
    private void log(LoggerVO loggerVO,String exceptionMsg){
    	logService.log(loggerVO, exceptionMsg);
    }

}
